SPDX-FileCopyrightText: 2025 Carl Elebaut & Camille Frejafon SPDX-FileCopyrightText: 2025 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
import bpy
import bmesh
import math
import random
from mathutils import Vector
import mathutils
bpy.ops.object.select_all(action="SELECT") # Sélectionner tous les objets
bpy.ops.object.delete(use_global=False) # Supprimer les objets
bpy.ops.outliner.orphans_purge(do_recursive=True) # Nettoyer les données orphelinesTaille d’un carré
taille_carre = 5 # 5 unités Blender (correspondant à 20 cm)Nom des colonnes et des lignes
colonnes = ["A", "B", "C", "D", "E"]
lignes = [1, 2, 3, 4, 5]Fonction pour créer un carré
def creer_carre(position, taille, nom):
bpy.ops.mesh.primitive_plane_add(size=taille, location=position)
bpy.context.object.name = nomFonction pour ajouter du texte
def ajouter_texte(position, texte, taille):
bpy.ops.object.text_add(location=position)
objet_texte = bpy.context.object
objet_texte.data.body = texte
objet_texte.scale = (taille, taille, taille)
objet_texte.name = f"Texte_{texte}"Fonction pour générer un texte aléatoire
def texte_aleatoire():
candidats = [1, 2, 3, 5]
choix = random.sample(
candidats, random.randint(1, len(candidats))
) # Choisir au moins un numéro au hasardAppliquer la règle : 2 et 3 ne peuvent pas apparaître ensemble
if 2 in choix and 3 in choix:
choix.remove(random.choice([2, 3])) # Retirer soit 2, soit 3
return " ".join(map(str, sorted(choix)))def texte_aleatoire2():
candidats = [1, 6]
choix = random.sample(candidats, random.randint(1, len(candidats)))
return " ".join(map(str, sorted(choix)))Code pour associer les chiffres (1, 2, 3, 4, 5, 6) à des formes (sinusoïde, cercles, etc.)
def sinusoide(location=(0, 0, 0)):Code de la sinusoïde
amplitude = 1 # Amplitude de la sinusoïde (5 cm)
longueur = 5 # Longueur totale de la sinusoïde (10 cm)
cycles = 3 # Nombre de cycles de la sinusoïde
segments = 100 # Nombre de segments pour une courbe fluide
points = []
for i in range(segments + 1):
x = (i / segments) * longueur - (longueur / 2)
y = amplitude * math.sin((i / segments) * cycles * 2 * math.pi)
z = 02D dans le plan XY
points.append((x, y, z))
mesh = bpy.data.meshes.new("SinusoidalMesh")
edges = [(i, i + 1) for i in range(len(points) - 1)]
mesh.from_pydata(points, edges, [])
mesh.update()
obj = bpy.data.objects.new("Sinusoidal", mesh)
bpy.context.collection.objects.link(obj)
obj.location = locationdef cercles(location=(0, 0, 0)):
nombre_cercles = 6
diametre_cercle = 1
rayon_cercle = diametre_cercle / 2
espace_x = 5 / (nombre_cercles - 1)
espace_y = 10 / (nombre_cercles - 1)
for i in range(nombre_cercles):
x = i * espace_x
y = i * espace_y - 4
bpy.ops.mesh.primitive_circle_add(
radius=rayon_cercle,
fill_type="NOTHING",
location=(x + location[0], y + location[1], location[2]),
)def cercles_aleatoires(location=(0, 0, 0)):Code des cercles aléatoires
nombre_cercles = 10
diametre_cercle = 1
rayon_cercle = diametre_cercle / 2
positions = []
while len(positions) < nombre_cercles:
x = random.uniform(-2.5, 2.5)
y = random.uniform(-10, 10)
z = 0
overlap = False
for pos in positions:
distance = math.sqrt((x - pos[0]) ** 2 + (y - pos[1]) ** 2)
if distance < diametre_cercle:
overlap = True
break
if not overlap:
positions.append((x, y))
bpy.ops.mesh.primitive_circle_add(
radius=rayon_cercle,
fill_type="NOTHING",
location=(x + location[0], y + location[1], location[2]),
)Récupérer l’objet créé
cercle = bpy.context.objectChanger le nom de l’objet dans les calques
cercle.name = "cercle_3"def cercles_4(location=(0, 0, 0)):Code des cercles de Bézier
for _ in range(8):
taille = random.uniform(0.5, 2)
position_x = random.uniform(-2.5, 2.5)
position_y = random.uniform(-10, 10)
bpy.ops.curve.primitive_bezier_circle_add(
radius=taille,
location=(position_x + location[0], position_y + location[1], location[2]),
)Récupérer l’objet créé
cercle = bpy.context.objectChanger le nom de l’objet dans les calques
cercle.name = "cercle_4"def points(location=(0, 0, 0)):Code des points
for _ in range(30):
x = random.uniform(-2.2, 2.5)
y = random.uniform(-10, 10)
bpy.ops.mesh.primitive_uv_sphere_add(
radius=0.05, location=(x + location[0], y + location[1], location[2])
)Récupérer l’objet créé
cercle = bpy.context.objectChanger le nom de l’objet dans les calques
cercle.name = "cercle_5"def zigzag(location=(0, 0, 0)):Code du zigzag
amplitude = 1
longueur = 8
cycles = 1
segments_per_cycle = 6
points = []
total_segments = cycles * segments_per_cycle
for i in range(total_segments + 1):
x = (i / total_segments) * longueur - (longueur / 2)
y = amplitude if i % 2 == 0 else -amplitude
z = 0
points.append((x, y, z))
mesh = bpy.data.meshes.new("AngularSinusoidalMesh")
edges = [(i, i + 1) for i in range(len(points) - 1)]
mesh.from_pydata(points, edges, [])
mesh.update()
obj = bpy.data.objects.new("zigzag2D", mesh)
bpy.context.collection.objects.link(obj)
obj.location = locationCHEMIN Fonction pour créer une courbe pour visualiser le chemin
def creer_chemin_courbe(chemin):Créer une nouvelle courbe
bpy.ops.curve.primitive_bezier_curve_add()
courbe = bpy.context.object
courbe.name = "Chemin"
courbe.data.dimensions = "3D"Supprimer les points par défaut
courbe.data.splines.clear()Ajouter une spline polyline pour le chemin
spline = courbe.data.splines.new(type="POLY")
spline.points.add(len(chemin) - 1) # Ajouter le nombre de points nécessaires
for i, (col, ligne) in enumerate(chemin):
col_index = colonnes.index(col)
ligne_index = lignes.index(ligne)
x = col_index * taille_carre
y = ligne_index * taille_carre
z = 0.1 # Légèrement au-dessus des cases
spline.points[i].co = (x, y, z, 1) # (x, y, z, w)Ajuster l’épaisseur de la courbe pour la rendre visible
courbe.data.bevel_depth = 0.1 # Épaisseur
courbe.data.bevel_resolution = 4 # Lissage de la courbeFonction pour générer un chemin respectant vos contraintes
def generer_chemin():Choisir une case de départ dans la ligne 4
col_depart = random.choice(colonnes)
case_depart = (col_depart, 5)Initialisation du chemin
chemin = [case_depart]
directions = [(1, 0), (-1, 0), (0, -1)] # Droite, gauche, haut (pas de diagonale)Générer le chemin
while True:
col, ligne = chemin[-1]
col_index = colonnes.index(col)
ligne_index = lignes.index(ligne)Chercher les cases voisines valides
voisins = []
for dx, dy in directions:
col_voisin_index = col_index + dx
ligne_voisin_index = ligne_index + dy
if 0 <= col_voisin_index < len(colonnes) and 0 <= ligne_voisin_index < len(
lignes
):
case_voisin = (colonnes[col_voisin_index], lignes[ligne_voisin_index])
if (
case_voisin not in chemin
): # Éviter de revenir sur une case déjà visitée
voisins.append(case_voisin)
if not voisins:
break # Si aucun voisin valide, arrêter le cheminAjouter une case voisine au hasard au chemin
case_suivante = random.choice(voisins)
chemin.append(case_suivante)
return cheminÉtape 2 : Création de la grille
for i, col in enumerate(colonnes):
for j, ligne in enumerate(lignes):Position de chaque carré
x = i * taille_carre
y = j * taille_carre
z = 0 # Les carrés sont plats sur le soAjout d’un carré (mesh plane)
bpy.ops.mesh.primitive_plane_add(size=taille_carre, location=(x, y, z))
carre_nom = f"{col}{ligne}"
bpy.context.object.name = carre_nom
if ligne == 5:
ajouter_texte((x, y, z + 0.1), "1", 1)
if ligne == 1:
if col == "D":
ajouter_texte((x, y, z + 0.1), "4", 1)
if ligne == 4:
if col == "E":
ajouter_texte((x, y, z + 0.1), "4", 1)Ajouter du texte aléatoire dans les cases
if col == "A" and ligne in [2, 3, 4]:
texte = texte_aleatoire()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "E" and ligne in [1, 2, 3]:
texte = texte_aleatoire()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "B" and ligne in [1, 3, 4]:
texte = texte_aleatoire()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "C" and ligne in [1, 2, 3, 4]:
texte = texte_aleatoire()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "D" and ligne in [2, 3, 4]:
texte = texte_aleatoire()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "B" and ligne in [2]:
texte = texte_aleatoire2()
ajouter_texte((x, y, z + 0.1), texte, 1)
if col == "A" and ligne in [1]:
texte = texte_aleatoire2()
ajouter_texte((x, y, z + 0.1), texte, 1)Étape 3 : Générer un chemin et le visualiser
chemin = generer_chemin()
creer_chemin_courbe(chemin)
offset_x = len(colonnes) * taille_carre + 2 # Déplacer à droite de la matrice existantePARTITION
formes_par_chiffre = {
1: sinusoide,
2: cercles,
3: cercles_aleatoires,
4: cercles_4,
5: points,
6: zigzag,
}
hauteurs_matrice = {
"A5": 0,
"B5": 2,
"C5": 3,
"D5": 2,
"E5": 0,
"A4": 10,
"B4": 10,
"C4": 10,
"D4": 10,
"E4": 7,
"A3": 10,
"B3": 10,
"C3": 10,
"D3": 10,
"E3": 7,
"A2": 7,
"B2": 7,
"C2": 7,
"D2": 7,
"E2": 7,
"A1": 7,
"B1": 5,
"C1": 0,
"D1": 5,
"E1": 7,
}Parcourir le chemin
for i, (col, ligne) in enumerate(chemin):
col_index = colonnes.index(col)
ligne_index = lignes.index(ligne)
x = col_index * taille_carre
y = ligne_index * taille_carreIdentifier la clé de la matrice (exemple : “A5”, “B4”, …)
case_id = f"{col}{ligne}"Récupérer le texte (chiffres) de la case traversée
texte_case = None
for obj in bpy.context.scene.objects:
if obj.type == "FONT" and (obj.location.x == x) and (obj.location.y == y):
texte_case = obj.data.body # Récupérer le texte brut
breakAjouter toutes les formes associées aux chiffres présents dans la case
if texte_case:
try:
chiffres = list(
map(int, texte_case.split())
) # Convertir le texte en une liste de chiffres
except ValueError:
chiffres = [] # Si une erreur de conversion se produit, ignorer cette caseAjouter les formes correspondant aux chiffres
for j, chiffre in enumerate(chiffres):
if chiffre in formes_par_chiffre:
forme = formes_par_chiffre[chiffre]
hauteur = hauteurs_matrice.get(
case_id, 0
) # Récupérer la hauteur associée à la caseDéfinir une hauteur spécifique pour le chiffre 1
if chiffre == 1 and case_id in hauteurs_matrice:
hauteur = hauteurs_matrice[case_id]Ajouter la forme avec la hauteur définie (en 2D)
forme(location=(offset_x + i * taille_carre + 10 + j * 2, hauteur, 0))CUBE
dimension = 20Position (x, y, z) en mètres
position = (150, 0, 10) # Par exemple : 10 cm sur X, 20 cm sur Y, 30 cm sur ZAjouter un cube
bpy.ops.mesh.primitive_cube_add(size=dimension, location=position)Optionnel : récupérer le cube créé pour d’autres modifications
cube = bpy.context.object
cube.name = "Cube"Ajouter une courbe sinusoïdale 3D
def create_sinusoidal():
min_x = 150 - 20 / 2
max_x = 150 + 20 / 2
min_y = 0 - 20 / 2
max_y = 0 + 20 / 2
min_z = 10 - 16 / 2
max_z = 10 + 16 / 2
center_x = 150
center_y = 0
center_z = 10Ajouter une courbe de Bézier
bpy.ops.curve.primitive_bezier_curve_add(location=(center_x, center_y, center_z))
curve = bpy.context.object
curve.name = "Sinusoidal 3D"Récupérer les points de contrôle
spline = curve.data.splines[0]
spline.bezier_points.add(10) # Ajouter des points pour la courbePlacer le premier point de la sinusoïde sur la face inférieure du cube
first_point = spline.bezier_points[0]
first_point.co = (
random.uniform(min_x, max_x) - center_x,
random.uniform(min_y, max_y) - center_y,
-10,
) # Position sur la face inférieure
first_point.handle_left_type = "AUTO"
first_point.handle_right_type = "AUTO"Calculer les autres points de la courbe sinusoïdale
for i, point in enumerate(
spline.bezier_points[1:], start=1
): # Commence après le premier pointPlacer ces points de façon aléatoire dans le cube
x = random.uniform(min_x, max_x) - center_x
y = random.uniform(min_y, max_y) - center_y
z = math.sin(i) * (max_z - min_z) / 2.5 # Application de la sinusoïde
point.co = (x, y, z)
point.handle_left_type = "AUTO"
point.handle_right_type = "AUTO"Convertir la courbe en tube
curve.data.bevel_depth = 1 # Épaisseur du tube
curve.data.bevel_resolution = 5 # Résolution du tube
create_sinusoidal()Positionner les points générés sur la face supérieure du cube
def points_sur_face_superieure(cube):Récupérer les dimensions et la position du cube
dimensions = cube.dimensions
position = cube.locationCalculer les bornes de la face supérieure et inférieure du cube
xmin = position.x - dimensions.x / 2
xmax = position.x + dimensions.x / 2
ymin = position.y - dimensions.y / 2
ymax = position.y + dimensions.y / 2
zmax = position.z + dimensions.z / 2 # Haut du cube
zmin = position.z - dimensions.z / 2 # Bas du cubeCréer une nouvelle collection pour les cercles
mesh = bpy.data.meshes.new("GeneratedMesh")
obj = bpy.data.objects.new("cercle_5_cube", mesh)
bpy.context.scene.collection.objects.link(obj)
bpy.context.view_layer.objects.active = objCréer un tableau pour placer les cercles
bm = bmesh.new()
for _ in range(60): # Générer 50 cercles aléatoires
x = random.uniform(-2.2, 2.7) # Garder la logique originale
y = random.uniform(-10, 10)Remapper les coordonnées dans les bornes du cube
x_mapped = xmin + (x + 2.2) / 5.5 * (xmax - xmin)
y_mapped = ymin + (y + 10) / 20 * (ymax - ymin)
z_mapped = zmaxCréer un cercle à l’emplacement calculé
circle_verts = []
num_segments = 16 # Nombre de segments pour le cercle
radius = 0.2 # Rayon du cercle
for i in range(num_segments):
angle = 2 * math.pi * i / num_segments
vert = bm.verts.new(
(
x_mapped + radius * math.cos(angle),
y_mapped + radius * math.sin(angle),
z_mapped,
)
)
circle_verts.append(vert)Connecter les sommets en une face circulaire
circle_face = bm.faces.new(circle_verts)
circle_face.normal_update()Extruder le cercle vers le bas
extrude_result = bmesh.ops.extrude_face_region(bm, geom=[circle_face])
extruded_geom = extrude_result["geom"]
verts_extruded = [v for v in extruded_geom if isinstance(v, bmesh.types.BMVert)]Calculer une extrusion aléatoire dans la limite du cube
extrusion_height = random.uniform(
1, zmax - 2
) # si je veux vers le bas remettre zmin
bmesh.ops.translate(
bm, verts=verts_extruded, vec=Vector((0, 0, -extrusion_height))
)Appliquer les modifications au maillage
bm.to_mesh(mesh)
mesh.update()Appeler la fonction pour positionner et extruder les cercles
points_sur_face_superieure(cube)Fonction pour créer un zigzag avec épaisseur en plan
def zigzag(location=(0, 0, 0), extrusion_length=5, largeur=0.5, epaisseur=0.5):Paramètres du zigzag
amplitude = 1
longueur = 8
cycles = 1
segments_per_cycle = 6
total_segments = cycles * segments_per_cycle
points_top = []
points_bottom = []Génération des points du zigzag avec une épaisseur
for i in range(total_segments + 1):
y = (i / total_segments) * longueur - (longueur / 2) # Zigzag sur Y
z = amplitude if i % 2 == 0 else -amplitude # Alternance sur Z
x = 0 # Plan fixe pour le zigzag avant extrusionAjouter des points avec épaisseur
points_top.append((x, y + epaisseur / 2, z))
points_bottom.append((x, y - epaisseur / 2, z))Relier les points pour former un maillage
points = points_top + points_bottom
faces = []Créer des faces entre les points top et bottom
for i in range(total_segments):
faces.append((i, i + 1, i + 1 + total_segments + 1, i + total_segments + 1))Création du maillage du zigzag
mesh = bpy.data.meshes.new("ZigzagMesh")
mesh.from_pydata(points, [], faces)
mesh.update()Ajouter le zigzag dans la scène
obj = bpy.data.objects.new("Zigzag", mesh)
bpy.context.collection.objects.link(obj)
obj.location = location # Appliquer la position spécifiéeExtrusion sur l’axe X
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode="EDIT") # Passer en mode édition
bpy.ops.mesh.extrude_region_move(
TRANSFORM_OT_translate={"value": (extrusion_length, 0, 0)}
)
bpy.ops.object.mode_set(mode="OBJECT") # Revenir en mode objetCalcul des limites des faces du cube
cube_min_y = position[1] - (dimension / 2)
cube_max_y = position[1] + (dimension / 2)
cube_min_x = position[0] - (dimension / 2)
cube_max_x = position[0] + (dimension / 2)
cube_min_z = position[2] - (dimension / 2)
cube_max_z = position[2] + (dimension / 2)Générer un ou deux zigzags de manière aléatoire
zigzag_count = random.randint(1, 2) # Générer 1 ou 2 zigzags
for _ in range(zigzag_count):Position aléatoire pour chaque zigzag
random_y = random.uniform(cube_min_y + 5, cube_max_y - 5) # Aléatoire sur Y
random_x = cube_max_x # Position fixe sur la face latérale (max X)
random_z = random.uniform(cube_min_z, cube_max_z - 1) # Aléatoire sur ZLongueur d’extrusion aléatoire (minimum 2, maximum dans les limites du cube)
max_extrusion_length = min(-dimension, 0) # Limite max raisonnable pour extrusion
extrusion_length = random.uniform(5, max_extrusion_length)Appeler la fonction zigzag avec une position aléatoire et extrusion aléatoire
zigzag(location=(random_x, random_y, random_z), extrusion_length=extrusion_length)— Forme Cercles (face Y parallèle, extrusion perpendiculaire, corrigé) —
def cercles(location=(0, 0, 0), group_name="Cercles_2_cube"):
nombre_cercles = 6
diametre_cercle = 1
rayon_cercle = diametre_cercle / 2
espace_x = 5 / (nombre_cercles - 1)
espace_z = 10 / (nombre_cercles - 1)
heights = [
2 + (13 / (nombre_cercles - 1)) * i for i in range(nombre_cercles)
] # Hauteurs graduelles de 2 à 15Créer une collection temporaire pour regrouper les cercles
bpy.ops.object.select_all(action="DESELECT") # Désélectionner tous les objets
for i, height in enumerate(heights):
x = i * espace_x - 2.5 # Décalage horizontal centré sur la face Y
z = i * espace_z - 4 # Décalage vertical
bpy.ops.mesh.primitive_circle_add(
radius=rayon_cercle,
fill_type="NOTHING",
location=(
location[0] + x,
location[1] - 0.1,
location[2] + z,
), # Ajustement de la position Y
rotation=(1.5708, 0, 0), # Rotation pour aligner parallèlement à la face Y
)Extrusion du cercle perpendiculairement à la face Y
bpy.ops.object.mode_set(mode="EDIT") # Passer en mode édition
bpy.ops.mesh.extrude_region_move(
TRANSFORM_OT_translate={
"value": (0, -height, 0)
} # Extruder vers l'intérieur (-Y)
)
bpy.ops.object.mode_set(mode="OBJECT") # Revenir en mode objet
bpy.context.object.name = (
f"{group_name}_Circle_{i}" # Nom unique pour chaque cercle
)Joindre tous les cercles dans un seul objet
bpy.ops.object.select_all(action="DESELECT") # Désélectionner tout
for obj in bpy.context.scene.objects:
if obj.name.startswith(group_name):
obj.select_set(True) # Sélectionner uniquement les cercles du groupe
bpy.context.view_layer.objects.active = bpy.context.selected_objects[
0
] # Activer le premier objet sélectionné
bpy.ops.object.join() # Joindre tous les cercles en un seul objet
bpy.context.object.name = group_name # Renommer l'objet groupéGénérer entre 1 et 4 groupes de cercles aléatoires sur la face Y
cercles_count = random.randint(1, 4) # Nombre de fois que les cercles apparaissent
for i in range(cercles_count):
random_z = random.uniform(
cube_min_z + 4, cube_max_z - 5
) # Position aléatoire sur Z
random_y = (
cube_max_y - 0.01
) # Position ajustée pour être légèrement à l'intérieur du cube
random_x = random.uniform(
cube_min_x + 2.5, cube_max_x - 2.5
) # Position aléatoire sur X
cercles(location=(random_x, random_y, random_z), group_name=f"Cercles_2_cube{i}")Fonction pour créer des cercles avec extrusion aléatoire
def cercles_aleatoires_extrudes(
location=(0, 0, 0),
nombre_cercles=30,
diametre_cercle=1,
min_extrude=2,
max_extrude=18,
):
rayon_cercle = diametre_cercle / 2
positions = []Création d’un nouveau maillage pour regrouper les cercles extrudés
mesh = bpy.data.meshes.new("CerclesMesh")
obj = bpy.data.objects.new("Cercles_3_cube", mesh)
bpy.context.collection.objects.link(obj)Liste des vertices, edges et faces pour construire le maillage
vertices = []
faces = []Génération des cercles et extrusion
for _ in range(nombre_cercles):Position du cercle confinée à la face -X
x = location[0] # Les cercles sont sur la face -X
y = random.uniform(-dimension / 2.1, dimension / 2.1) # Confinement en Y
z = random.uniform((-dimension / 2) + 11, dimension) # Confinement en ZLongueur d’extrusion aléatoire
extrusion = random.uniform(min_extrude, max_extrude)Génération des vertices pour un cercle
start_index = len(vertices)
num_segments = 32 # Pour un cercle "lisse"
circle_vertices = []
for i in range(num_segments):
angle = 2 * math.pi * i / num_segments
vx = x
vy = y + math.cos(angle) * rayon_cercle
vz = z + math.sin(angle) * rayon_cercle
circle_vertices.append((vx, vy, vz))Ajouter les vertices du cercle de base
vertices.extend(circle_vertices)Ajouter les vertices extrudés (parallèle à l’axe X)
extruded_vertices = [(vx + extrusion, vy, vz) for vx, vy, vz in circle_vertices]
vertices.extend(extruded_vertices)Ajouter les faces entre les cercles de base et extrudés
for i in range(num_segments):
next_i = (i + 1) % num_segments
base_1 = start_index + i
base_2 = start_index + next_i
extruded_1 = base_1 + num_segments
extruded_2 = base_2 + num_segmentsFace latérale
faces.append([base_1, base_2, extruded_2, extruded_1])Face du cercle de base (optionnelle)
faces.append([start_index + i for i in range(num_segments)])Face du cercle extrudé (optionnelle)
faces.append([start_index + num_segments + i for i in range(num_segments)])Construire le maillage regroupé
mesh.from_pydata(vertices, [], faces)
mesh.update()
return objAjouter des cercles extrudés aléatoires sur la face -X
cercles_aleatoires_extrudes(
location=(position[0] - dimension / 2, position[1], position[2]),
nombre_cercles=30, # Ajuste selon le nombre souhaité
diametre_cercle=1, # Diamètre constant des cercles
min_extrude=1, # Longueur minimale de l'extrusion
max_extrude=18, # Longueur maximale de l'extrusion
)Fonction pour ajouter des cercles sur la face -Y du cube
def cercles_4(location=(0, 0, 0)):Définir les limites de la face -Y du cube
face_y = location[1] - dimension / 2 # La position de la face -Y sur l'axe Y
face_x_min = location[0] - dimension / 2 # Limite min de la face sur l'axe X
face_x_max = location[0] + dimension / 2 # Limite max de la face sur l'axe X
face_z_min = location[2] - dimension / 2 # Limite min de la face sur l'axe Z
face_z_max = location[2] + dimension / 2 # Limite max de la face sur l'axe Z
for index in range(20): # nombre en tout
taille = random.uniform(0.5, 1.5) # Taille du cercle
position_x = random.uniform(face_x_min + taille, face_x_max - taille)
position_z = random.uniform(
face_z_min + taille, face_z_max - taille
) # Placer sur l'axe ZAjouter un cercle de Bézier sur la face -Y
bpy.ops.curve.primitive_bezier_circle_add(
radius=taille, location=(position_x, face_y, position_z)
)Récupérer l’objet créé
cercle = bpy.context.object
cercle.rotation_euler = (
1.5708,
0,
0,
) # Rotation de 90° (1.5708 rad) autour de l'axe XDonner un nom spécifique à l’objet
cercle.name = (
f"cercle_4_cube{index + 1}" # Exemple : cercle_4_1, cercle_4_2, ...
)Convertir le cercle de Bézier en mesh
bpy.ops.object.convert(target="MESH")Extrusion aléatoire entre 2 et 18 unités
extrusion_height = random.uniform(2, 18) # longueurCalculer la direction perpendiculaire à la face -Y du cube (normale de la face)
extrusion_direction = (
0,
1,
0,
) # La direction perpendiculaire à la face -Y est sur l'axe Y
extrusion_direction = mathutils.Vector(extrusion_direction)Extruder l’objet selon cette direction
bpy.ops.object.mode_set(mode="EDIT") # Passer en mode édition
bpy.ops.mesh.select_all(action="SELECT") # Sélectionner tous les vertices
bpy.ops.mesh.extrude_region_move(
TRANSFORM_OT_translate={"value": extrusion_direction * extrusion_height}
) # Extruder
bpy.ops.object.mode_set(mode="OBJECT") # Revenir en mode objetAppeler la fonction pour ajouter les cercles et les extruder en tubes
cercles_4(location=position)Supprimer le cube tout en maintenant les autres objets
bpy.data.objects.remove(cube, do_unlink=True)